//
//  GQJWeakController.m
//  TotalDemo
//
//  Created by gujian on 16/8/2.
//  Copyright © 2016年 gujian. All rights reserved.
//

#import "GQJWeakController.h"
#import <objc/message.h>

OBJC_EXTERN int _objc_rootRetainCount(id);
OBJC_EXTERN void _objc_autoreleasePoolPrint();

@implementation GQJWeakController

- (instancetype)init
{
    self = [super init];
    if (self) {
        NSLog(@"self.class:%@",NSStringFromClass([self class]));
        NSLog(@"self.superClass:%@",NSStringFromClass([super class]));
    }
    
    return self;
}

- (void)loadView
{
    [super loadView];

}

- (void)viewDidLoad
{
    [super viewDidLoad];
    
//    [self testFunction];
    
//    [self testRegisterToAutoRelease];
//    _objc_autoreleasePoolPrint();
    
    objc_msgSend(self, @selector(testRegisterToAutoRelease1));
}

//测试weak修饰的变量向autoreleasePool注册了几次
- (void)testRegisterToAutoRelease
{
    //1.
    id obj = [[NSObject alloc] init];
    id __weak o = obj;
    _objc_autoreleasePoolPrint();

    NSLog(@"1 %@", o);
    //汇编代码为
    //_objc_loadWeakRetained
    //_NSLog
    //_objc_release
    NSLog(@"2 %@", o);
    NSLog(@"3 %@", o);
    NSLog(@"4 %@", o);
    NSLog(@"5 %@", o);
    
    _objc_autoreleasePoolPrint();
    
    //2
    //等价于
    //_objc_loadWeakRetained
    //_NSLog _NSLog _NSLog _NSLog _NSLog
    //_objc_release
    
    //这样实现的原因参考
    //http://stackoverflow.com/questions/8148551/objective-c-weak-property-getter-autoreleases-automatic-reference-counting
    //http://clang.llvm.org/docs/AutomaticReferenceCounting.html#about-this-document 4.2
    
    //Reading occurs when performing a lvalue-to-rvalue conversino on an object lvalue
    //1. For __weak objects, the current pointee is retained and then released at the end of
    //the current full-expression. This must execute atomically with respect to assignments and
    //to the final rlease of the pointee.
    //2. For all other objects, the lvalue is loaded with primitive semantics.
    
    
    id temp = o;
    NSLog(@"1 %@", temp);
    NSLog(@"2 %@", temp);
    NSLog(@"3 %@", temp);
    NSLog(@"4 %@", temp);
    NSLog(@"5 %@", temp);
    
    //1与2的区别可从汇编代码中看出区别，每次直接使用__weak修饰的变量都会调用
    //_objc_loadWeakRetained方法
    //使用完之后会调用_objc_release
}

- (void)testRegisterToAutoRelease1
{
    @autoreleasepool {
        id obj = [[NSObject alloc] init];
        _objc_autoreleasePoolPrint();
        
        id __weak o = obj;
        NSLog(@"before using __weak: reatin count = %d", _objc_rootRetainCount(obj));
        NSLog(@"class = %@", [o class]);
        NSLog(@"after using __weak: retain count = %d", _objc_rootRetainCount(obj));
        _objc_autoreleasePoolPrint();
    }
}


- (void)testFunction
{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    GQJWeakModel *model = [[GQJWeakModel alloc] init];
    
    NSLog(@"1model.retainCount:%d",_objc_rootRetainCount(model));
    GQJWeakModel * __weak weakModel = model;
    NSLog(@"2model.retainCount:%d",_objc_rootRetainCount(model));
    
    
//    //logInfo:threadName:方法不会完全执行完，取决于weakModel何时置nil
//    //具有不确定性
//    dispatch_async(queue, ^{
//        
//        for (int i = 0; i < 1000; i++) {
//            [weakModel logInfo:i threadName:@"thread1"];
//            NSLog(@"%@",model);
//        }
//        
//    });
    
//    下面的方法会使得weakModel免于过早释放，使引用对象的引用计数+1
    dispatch_async(queue, ^{
       
        GQJWeakModel *strongModel = weakModel;

        for (int i = 0; i < 1000; i++) {
            [strongModel logInfo:i threadName:@"thread2"];
        }
        
        strongModel = nil;

    });
    
    
    __block int variable = 10;
    
    void (^Blk)(void) = ^{
        int variable = 20;
        variable = 20;
    };
    
    Blk();
    
    NSLog(@"variable:%d",variable);
    
    
    for (int i = 0; i < 100; i++) {
        NSLog(@"main i :%d",i);
        if (i == 50) {
            //1.证明了Block里面的strongModel会增加model的引用计数
            //即强引用一个弱引用会增加最终对象的引用计数
            //2.证明了Block会截获对象，并增加对象的引用计数
            NSLog(@"3.mode.retainCount:%d",_objc_rootRetainCount(model));
            
            
            model = nil;
            break;
        }
    }
 
}

@end


@implementation GQJWeakModel

- (void)logInfo:(NSInteger)num threadName:(NSString *)name
{
//    for (int i = 0; i < 1000 ; i++) {
//        NSLog(@"i:%d",i);
//    }
    NSLog(@"thread->%@i:%ld",name,(long)num);
}

@end